package com.bxl.chooser.context;

import com.bxl.chooser.bean.AppInfo;
import com.bxl.chooser.client.ChooseClientContext;
import com.bxl.chooser.client.ChooseClientContextImpl;
import com.bxl.chooser.client.Partners;
import com.bxl.chooser.server.ChooseServerContext;
import com.bxl.chooser.server.ChooseServerContextImpl;
import com.bxl.chooser.server.SlidingWindow;
import com.bxl.chooser.util.RandomUtil;
import com.bxl.chooser.util.ThreadUtil;
import com.bxl.netty.rpc.client.common.client.ClientInfo;
import com.bxl.netty.rpc.common.Lifecycle;
import com.bxl.netty.rpc.common.pool.ThreadPoolUtil;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * <>
 *
 * @author baixl
 * @date 2021/3/25
 */
public class ChooserContext {
    private AppInfo appInfo;
    //默认秒
    private long baseInterval;
    private int scopeInterval = 5;
    private ChooseServerContext serverContext;
    private ChooseClientContext clientContext;
    private SlidingWindow slidingWindow;
    //伙伴
    private Partners partners;
    private int port = 9090;
    private ScheduledFuture scheduledFuture;
    private boolean hasLeader = false;
    private ClientInfo leader = new ClientInfo("LEADER", "0", 0, null);

    public void init() {
        initPort();
        initBaseInterval();
        initAppInfo();
        initServerContext();
        initClientContext();
        initSlidingWindow();
        initPartners();
        initThread();
    }

    protected void initPort() {
        try {
            //this.port = Integer.valueOf(System.getProperty("Port"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ChooserContext chooserContext = new ChooserContext();
        chooserContext.init();
    }

    protected void initSlidingWindow() {
        slidingWindow = new SlidingWindow(30, 1, TimeUnit.SECONDS);
        slidingWindow.init();
    }

    protected void initServerContext() {
        serverContext = new ChooseServerContextImpl(this.port);
        serverContext.init();
        serverContext.setChooserContext(this);
        if (serverContext instanceof Lifecycle) {
            Lifecycle lifecycle = (Lifecycle) serverContext;
            Thread thread = new Thread(() -> {
                lifecycle.start();
            });
            thread.setDaemon(true);
            thread.start();

        }
    }

    protected void initClientContext() {
        clientContext = new ChooseClientContextImpl();
        clientContext.init();
    }

    protected void initPartners() {
        partners = new Partners(this);
    }

    protected void initBaseInterval() {
        this.baseInterval = 30;
    }

    protected void initAppInfo() {
        this.appInfo = new AppInfo(AppInfo.AppState.FOLLOWER, 0, 0, 0, 0, null, String.valueOf(port)) /*AppInfo.builder().term(0).commitLogIndex(0)
                .commitTerm(0).logIndex(0).state(AppInfo.AppState.FOLLOWER).build()*/;
    }

    public void initThread() {
        long interval = RandomUtil.random(baseInterval, scopeInterval);
        scheduledFuture = ThreadPoolUtil.scheduleAtFixedRate(new HeartBeatMoniterTask(this), interval, interval, TimeUnit.SECONDS);

    }


    private static class HeartBeatMoniterTask implements Runnable {
        private ChooserContext chooserContext;

        public HeartBeatMoniterTask(ChooserContext chooserContext) {
            this.chooserContext = chooserContext;
        }

        @Override
        public void run() {
            //没有收到心跳信息
            if (chooserContext.getSlidingWindow().stts(chooserContext.getLeader()) <= 0) {
                chooserContext.setHasLeader(false);
                //改变状态，投票给自己
                if (chooserContext.getAppInfo().voteSelf()) {
                    //推荐其他server投票给自己
                    if (chooserContext.getPartners().vote() > (chooserContext.getPartners().getPartnersNum() / 2)) {
                        chooserContext.getAppInfo().termIncrease();
                        //获得选举 自己升级为leader
                        chooserContext.getAppInfo().leader();
                        chooserContext.setLeader(new ClientInfo("CHOOSE", "localhost", chooserContext.getPort(), chooserContext.getClientContext()));
                        chooserContext.setHasLeader(true);
                        //取消定时任务
                        if (chooserContext.getScheduledFuture() != null) {
                            chooserContext.getScheduledFuture().cancel(true);
                        }

                        //心跳
                        chooserContext.setScheduledFuture(ThreadUtil.scheduleWithFixedDelay(() -> {
                            chooserContext.getPartners().heartBeat();
                        }, 0, chooserContext.getBaseInterval() / 3, TimeUnit.SECONDS));
                        //开始同步数据提交
                        chooserContext.getPartners().startCommit(chooserContext.getAppInfo());
                    } else {
                        //投票没有通过，恢复角色
                        chooserContext.getAppInfo().follower();

                        if (chooserContext.getScheduledFuture() != null) {
                            chooserContext.getScheduledFuture().cancel(true);
                        }
                        long newInterval = RandomUtil.random(chooserContext.getBaseInterval(), chooserContext.getScopeInterval());
                        //修改心跳的时间
                        chooserContext.setScheduledFuture(ThreadPoolUtil.scheduleAtFixedRate(this, newInterval, newInterval, TimeUnit.SECONDS));
                    }
                } else {
                    //已经投票给别人，继续等待leader的心跳
                }
            }
        }
    }


    public synchronized void degraded() {
        if (appInfo.getState() == AppInfo.AppState.LEADER) {
            appInfo.follower();
            //停止进行数据同步
            partners.stopSync();
            //停止发送提交信息
            partners.stopCommit();
            if (scheduledFuture != null) {
                scheduledFuture.cancel(true);
            }
            //等待心跳
            initThread();

        }
    }

    public String getIp() {
        return "localhost";
    }

    public int getPort() {
        return port;
    }

    public AppInfo getAppInfo() {
        return appInfo;
    }

    public void setAppInfo(AppInfo appInfo) {
        this.appInfo = appInfo;
    }

    public long getBaseInterval() {
        return baseInterval;
    }

    public void setBaseInterval(long baseInterval) {
        this.baseInterval = baseInterval;
    }

    public int getScopeInterval() {
        return scopeInterval;
    }

    public void setScopeInterval(int scopeInterval) {
        this.scopeInterval = scopeInterval;
    }

    public ChooseServerContext getServerContext() {
        return serverContext;
    }

    public void setServerContext(ChooseServerContext serverContext) {
        this.serverContext = serverContext;
    }

    public ChooseClientContext getClientContext() {
        return clientContext;
    }

    public void setClientContext(ChooseClientContext clientContext) {
        this.clientContext = clientContext;
    }

    public SlidingWindow getSlidingWindow() {
        return slidingWindow;
    }

    public void setSlidingWindow(SlidingWindow slidingWindow) {
        this.slidingWindow = slidingWindow;
    }

    public Partners getPartners() {
        return partners;
    }

    public void setPartners(Partners partners) {
        this.partners = partners;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public ScheduledFuture getScheduledFuture() {
        return scheduledFuture;
    }

    public void setScheduledFuture(ScheduledFuture scheduledFuture) {
        this.scheduledFuture = scheduledFuture;
    }

    public boolean getHasLeader() {
        return hasLeader;
    }

    public void setHasLeader(boolean hasLeader) {
        this.hasLeader = hasLeader;
    }

    public ClientInfo getLeader() {
        return leader;
    }

    public void setLeader(ClientInfo leader) {
        this.leader = leader;
    }
}
